{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Define configuration variables and generator to read the input images"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Found 6000 images belonging to 3 classes.\n",
      "Found 1500 images belonging to 3 classes.\n"
     ]
    }
   ],
   "source": [
    "# import the needed libraries\n",
    "import numpy as np\n",
    "from tensorflow.keras.models import Sequential\n",
    "from tensorflow.keras.layers import Dense, Conv2D, MaxPool2D, Flatten, Activation\n",
    "from tensorflow.keras import backend as K\n",
    "from tensorflow.keras.preprocessing.image import ImageDataGenerator\n",
    "\n",
    "# config\n",
    "img_width, img_height = 28,28 #width & height of input image\n",
    "input_depth = 1 #1: gray image\n",
    "train_data_dir = 'dataset/mnist/training' #data training path\n",
    "testing_data_dir = 'dataset/mnist/testing' #data testing path\n",
    "epochs = 2 #number of training epoch\n",
    "batch_size = 5 #training batch size\n",
    "\n",
    "# define image generator for Keras,\n",
    "# here, we map pixel intensity to 0-1\n",
    "train_datagen = ImageDataGenerator(rescale=1/255)\n",
    "test_datagen = ImageDataGenerator(rescale=1/255)\n",
    "\n",
    "# read image batch by batch\n",
    "train_generator = train_datagen.flow_from_directory(\n",
    "    train_data_dir,\n",
    "    color_mode='grayscale',#inpput iameg: gray\n",
    "    target_size=(img_width,img_height),#input image size\n",
    "    batch_size=batch_size,#batch size\n",
    "    class_mode='categorical')#categorical: one-hot encoding format class label\n",
    "testing_generator = test_datagen.flow_from_directory(\n",
    "    testing_data_dir,\n",
    "    color_mode='grayscale',\n",
    "    target_size=(img_width,img_height),\n",
    "    batch_size=batch_size,\n",
    "    class_mode='categorical')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Define the network"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "input_tensor (Conv2D)        (None, 28, 28, 20)        520       \n",
      "_________________________________________________________________\n",
      "activation (Activation)      (None, 28, 28, 20)        0         \n",
      "_________________________________________________________________\n",
      "max_pooling2d (MaxPooling2D) (None, 14, 14, 20)        0         \n",
      "_________________________________________________________________\n",
      "conv2d (Conv2D)              (None, 14, 14, 20)        10020     \n",
      "_________________________________________________________________\n",
      "activation_1 (Activation)    (None, 14, 14, 20)        0         \n",
      "_________________________________________________________________\n",
      "max_pooling2d_1 (MaxPooling2 (None, 7, 7, 20)          0         \n",
      "_________________________________________________________________\n",
      "conv2d_1 (Conv2D)            (None, 7, 7, 20)          10020     \n",
      "_________________________________________________________________\n",
      "activation_2 (Activation)    (None, 7, 7, 20)          0         \n",
      "_________________________________________________________________\n",
      "flatten (Flatten)            (None, 980)               0         \n",
      "_________________________________________________________________\n",
      "dense (Dense)                (None, 10)                9810      \n",
      "_________________________________________________________________\n",
      "output_tensor (Dense)        (None, 3)                 33        \n",
      "=================================================================\n",
      "Total params: 30,403\n",
      "Trainable params: 30,403\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n"
     ]
    }
   ],
   "source": [
    "# define number of filters and nodes in the fully connected layer\n",
    "NUMB_FILTER_L1 = 20\n",
    "NUMB_FILTER_L2 = 20\n",
    "NUMB_FILTER_L3 = 20\n",
    "NUMB_NODE_FC_LAYER = 10\n",
    "\n",
    "#define input image order shape\n",
    "if K.image_data_format() == 'channels_first':\n",
    "    input_shape_val = (input_depth, img_width, img_height)\n",
    "else:\n",
    "    input_shape_val = (img_width, img_height, input_depth)\n",
    "\n",
    "#define the network\n",
    "model = Sequential()\n",
    "\n",
    "# Layer 1\n",
    "model.add(Conv2D(NUMB_FILTER_L1, (5, 5), \n",
    "                 input_shape=input_shape_val, \n",
    "                 padding='same', name='input_tensor'))\n",
    "model.add(Activation('relu'))\n",
    "model.add(MaxPool2D((2, 2)))\n",
    "\n",
    "# Layer 2\n",
    "model.add(Conv2D(NUMB_FILTER_L2, (5, 5), padding='same'))\n",
    "model.add(Activation('relu'))\n",
    "model.add(MaxPool2D((2, 2)))\n",
    "\n",
    "# Layer 3\n",
    "model.add(Conv2D(NUMB_FILTER_L3, (5, 5), padding='same'))\n",
    "model.add(Activation('relu'))\n",
    "\n",
    "# flattening the model for fully connected layer\n",
    "model.add(Flatten())\n",
    "\n",
    "# fully connected layer\n",
    "model.add(Dense(NUMB_NODE_FC_LAYER, activation='relu'))\n",
    "\n",
    "# output layer\n",
    "model.add(Dense(train_generator.num_classes, \n",
    "                activation='softmax', name='output_tensor'))\n",
    "\n",
    "# Compilile the network\n",
    "model.compile(loss='categorical_crossentropy',\n",
    "              optimizer='sgd', metrics=['accuracy'])\n",
    "\n",
    "# Show the model summary\n",
    "model.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Train the network"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/2\n",
      "1200/1200 [==============================] - 5s 5ms/step - loss: 0.1711 - acc: 0.9273 - val_loss: 0.0585 - val_acc: 0.9793\n",
      "Epoch 2/2\n",
      "1200/1200 [==============================] - 4s 4ms/step - loss: 0.0375 - acc: 0.9880 - val_loss: 0.0321 - val_acc: 0.9893\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<tensorflow.python.keras.callbacks.History at 0x7f50380b9668>"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Train and test the network\n",
    "model.fit_generator(\n",
    "    train_generator,#our training generator\n",
    "    #number of iteration per epoch = number of data / batch size\n",
    "    steps_per_epoch=np.floor(train_generator.n/batch_size),\n",
    "    epochs=epochs,#number of epoch\n",
    "    validation_data=testing_generator,#our validation generator\n",
    "    #number of iteration per epoch = number of data / batch size\n",
    "    validation_steps=np.floor(testing_generator.n / batch_size))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Save the trained model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Training is done!\n",
      "Model is successfully stored!\n"
     ]
    }
   ],
   "source": [
    "print(\"Training is done!\")\n",
    "model.save('./model/modelLeNet5.h5')\n",
    "print(\"Model is successfully stored!\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:TF1120_GPU]",
   "language": "python",
   "name": "conda-env-TF1120_GPU-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
